今天要做的是 Type ahead, 當你打下關鍵字的時候,會幫你找到,有這些關鍵字的城市。
這是我很期待練習的主題,因為我不太熟,多練習多練習。
今天的重點主要放在觀念的釐清。
Wes Bos 在 github 內附上了 cities.json
的檔案,這個就是他用 AJAX 去 load 回來的資料。
使用純 JS 去呼叫 API
可以使用 HttpRequest 和 fetch
如果我們使用 HttpRequest,是 callback 的方式,對我來說,這個是比較容易理解的方式。寫法可以上 w3school 去複習一下。
function requestHandler(){
console.log(JSON.parse(this.response))
}
let req = new XMLHttpRequest();
req.addEventListener('load',requestHandler)
req.open('get',endpoint)
req.send()
Wes Bos 在這個練習上用了 fetch,可以看到用 fetch 需要搭配使用 Promise,我們來理解一下下面這段 code 的意思
因為 const 是 ES6 後有的東西,所以 Wes Bos 在這裡使用了 const 他應該是想要防範 cities 被複寫。
const cities = [];
fetch(endpoint)
.then(blob => blob.json())
.then(data => cities.push(...data));
那除了上面這個寫法,可以改成更直觀一點的 let 寫法,如果是我的話,我為選擇下面的寫法。
let cities = null;
fetch(endpoint)
.then(blob => blob.json())
.then(data => (cities = data));
上面就是資料的部分。
因為他的 search 是用打字來找關鍵字,事件就用 keyup 來做
document.querySelector('.search').addEventListener('keyup',inputHandler)
正規表達式 MDN
在第一個 function 可以使用 Wes Bos 的方式,也可以用 retuen x.tolocaleString()
來做到一樣的效果。
來解釋一下,這段的意思:
(/\B(?=(\d{3})+(?!\d))/g, ',')
\B: (\d{3})
的前面要有東西,要防止 ,123 被 match 的情況出現。
\d:阿拉伯數字 (\d{3})
表示 3 位數,要一個逗號
{}:表示個數要出現的次數
+:至少一次
(?!\d):從最後一個數字的右側(結尾)開始
整句的解釋是:
從字串結尾開始, 在每三個數字一組並加上逗號。
Regexper 可以找到圖解
接著因為要比對是否有符合的字,Wes Bos 用 findMatches 來尋找符合的部分,下面可以找到 'gi'
g 就是全域搜尋, i 就是不分大小寫。
這裡用 map 的原因是因為,每個 city 會產生一個 html, 所以要用 map 把這幾組的 HTML 做成陣列。
Wes Bos 使用的 join 方法使用空格把資料合起來。
找到了關鍵字後,使用 replace 的方式套用 h1 的 class 換字的背景顏色。
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function inputHandler(){
const matchArray = findMatches(this.value, cities);
const html = matchArray
.map(place => {
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="population">${numberWithCommas(place.population)}</span>
</li>
`;
}).join('');
suggestions.innerHTML = html;
}
function findMatches(wordToMatch, cities) {
return cities.filter(place => {
const regex = new RegExp(wordToMatch, 'gi');
return place.city.match(regex) || place.state.match(regex)
});
}
以上,明天見